#pragma once
#include<queue>


template<class T>
class block_queue
{
    static const int defaultnum = 20;
public:
    block_queue(int maxcap = defaultnum)
    :maxcap_(maxcap)
    {
        pthread_mutex_init(&mutex_,nullptr);
        pthread_cond_init(&pcond_,nullptr);
        pthread_cond_init(&ccond_,nullptr);
        highlevel_ = (maxcap_*3)/4;
        lowlevel_ = maxcap_ / 4;
    }

    ~block_queue()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&pcond_);
        pthread_cond_destroy(&ccond_);
    }


    void push(const T& in)
    {
        pthread_mutex_lock(&mutex_);
        while(bq_.size() == maxcap_)
        {
            pthread_cond_wait(&pcond_,&mutex_);
        }

        bq_.push(in);
        if(bq_.size() >= highlevel_) pthread_cond_signal(&ccond_);

        pthread_mutex_unlock(&mutex_);
    }

    T pop()
    {
        pthread_mutex_lock(&mutex_);

        while(bq_.size() == 0)
        {
            pthread_cond_wait(&ccond_,&mutex_);
        }

        T out = bq_.front();
        bq_.pop();
        if(bq_.size() < lowlevel_) pthread_cond_signal(&pcond_);

        pthread_mutex_unlock(&mutex_);

        return out;
    }

private:
    std::queue<T> bq_;
    int maxcap_;
    int highlevel_;
    int lowlevel_;
    pthread_mutex_t mutex_;
    pthread_cond_t pcond_;
    pthread_cond_t ccond_;
};